#ifndef __M_QUEUE_H__
#define __M_QUEUE_H__
#include "../MQcommon/helper.hpp"
#include "../MQcommon/mq_msg.pb.h"
#include"../MQcommon/logger.hpp"

#include <google/protobuf/map.h>
#include <iostream>
#include <unordered_map>
#include <mutex>
#include <memory>
#include <vector>

namespace bitmq{
    struct MsgQueue{
        using ptr=std::shared_ptr<MsgQueue>;
        std::string name;//队列名称
        bool durable;//是否可持久化
        bool exclusive;//是否独占
        bool auto_delete;//是否自动删除
        google::protobuf::Map<std::string,std::string> args;
        MsgQueue(){}

        MsgQueue(const std::string &qname, 
            bool qdurable, 
            bool qexclusive,
            bool qauto_delete,
            const google::protobuf::Map<std::string, std::string> &qargs):
            name(qname), durable(qdurable), exclusive(qexclusive),
            auto_delete(qauto_delete), args(qargs){}

        void srtArgs(const std::string& target){
            std::vector<std::string> str_vc;
            std::string key;
            int pos=0;
            bitmq::StrHelper::stringSpilt(target,"&",str_vc);
            for(auto& e:str_vc){
                pos=e.find("=");
                key=e.substr(0,pos);
                args[key]=e.substr(pos+1);
            }
        }

        std::string getArgs(){
            std::string result;
            for(auto& x:args){
                result+=x.first+"="+x.second+"&";
            }
            return result;
        }

    };
    using QueueMap=std::unordered_map<std::string,MsgQueue::ptr>;
    class MsgQueueMapper{
        public:
        MsgQueueMapper(const std::string& dbfile):sql_helper(dbfile){
            std::string path=FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(path);
            sql_helper.open();
            createTbale();
        }
        void createTbale(){
             std::stringstream river;
             river<<"create table if not exists queue_table";
             river<<"(name varchar(32) primary key,durable bool,exclusive bool,auto_delete bool,args varchar(128));";
             bool ret=sql_helper.exec(river.str(),nullptr,nullptr);
             if(ret == false)
             {
                ELOG("创建表[%s]失败","queue_table");
                abort();
             }
        }
        void removeTable(){
            #define DELETE_QUEUE_TABLE "drop table if exists queue_table;"
            bool ret=sql_helper.exec(DELETE_QUEUE_TABLE,nullptr,nullptr);
            if(ret == false){
                ELOG("删除表[%s]失败","queue_table");
                abort();
            }
        }
        bool insert(MsgQueue::ptr& queue){
            std::stringstream sql;
            sql << "insert into queue_table values(";
            sql << "'" << queue->name << "', ";
            sql << queue->durable << ", ";
            sql << queue->exclusive << ", ";
            sql << queue->auto_delete << ", ";
            sql << "'" << queue->getArgs() << "');";
            return sql_helper.exec(sql.str(), nullptr, nullptr);

        }
        void remove(const std::string& name){
            std::stringstream sql;
            sql<<"delete from queue_table where name=";
            sql<<"'"<<name<<"';";
            sql_helper.exec(sql.str(),nullptr,nullptr);
        }
        QueueMap recover(){
            QueueMap result;
            std::string sql = "select name, durable, exclusive, auto_delete, args from queue_table;";
            sql_helper.exec(sql, selectCallBack, &result);
            return result;
        }
        private:
        static int selectCallBack(void* args,int count,char**row,char**filds)
        {
            QueueMap* result=(QueueMap*)args;
            auto it=std::make_shared<MsgQueue>();
            it->name=row[0];
            it->durable=(bool)std::stoi(row[1]);
            it->exclusive=(bool)std::stoi(row[2]);
            it->auto_delete=(int)std::stoi(row[3]);
            if(row[4]) it->srtArgs(row[4]);
            result->insert(std::make_pair(it->name,it));
            return 0;
        }
        bitmq::Sqlite3Helper sql_helper;
    };
    class MsgQueueManager{
        public:
        using ptr=std::shared_ptr<MsgQueueManager>;
        MsgQueueManager(const std::string& dbfile ):_helper(dbfile){
            _qmap=_helper.recover();
        }
        bool declareQueue(const std::string &name, 
            bool qdurable, 
            bool qexclusive,
            bool qauto_delete,
            const google::protobuf::Map<std::string, std::string> &qargs){
            std::unique_lock<std::mutex> _lock;
            auto it=_qmap.find(name);
            if(it != _qmap.end()){
                DLOG("该队列[%s]已经存在，不用重复创建！",name.c_str());
                return false;
            }
            auto queue=std::make_shared<MsgQueue>();
            queue->name=name;
            queue->durable=qdurable;
            queue->exclusive=qexclusive;
            queue->auto_delete=qauto_delete;
            queue->args=qargs;
            if(qdurable == true){
                _helper.insert(queue);
            }
            auto result=_qmap.insert(std::make_pair(name,queue));
            if(result.second){
              DLOG("插入队列[%s]成功!",name.c_str());
              return true;
            }
            return false;
        }
        void deleteQueue(const std::string &name){
            std::unique_lock<std::mutex> _lock;
             auto x=_qmap.find(name);
             if(x == _qmap.end()){
                DLOG("指定删除的队列[%s]不存在！",name);
             }
             if(_qmap[name]->durable){
                _helper.remove(name);
             }
             _qmap.erase(name);
        }
        MsgQueue::ptr selectQueue(const std::string &name){
            std::unique_lock<std::mutex> _lock;
            auto x=_qmap.find(name);
             if(x == _qmap.end()){
                DLOG("指定获取的队列[%s]不存在！",name);
                return MsgQueue::ptr();
             }
             return _qmap[name];
           
        }
        QueueMap allQueues(){
            std::unique_lock<std::mutex> _lock;
            if(_qmap.empty()){
                DLOG("暂时没有队列");
                return  QueueMap();
            }
            return _qmap;

        }
        bool exists(const std::string &name){
            std::unique_lock<std::mutex> _lock;
            auto x = _qmap.find(name);
            if (x == _qmap.end()){
                DLOG("队列[%s]不存在！",name.c_str());
                return false;
            }
            return true;
        }
        size_t size(){
            return _qmap.size();
        }
        void clear(){
           _helper.removeTable();
           _qmap.clear();
        }
        private:
        std::mutex _mutex;
        MsgQueueMapper _helper;
        QueueMap _qmap;
    };
}
#endif